package iagovsothello.logger;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Observable;
import java.util.Observer;

public abstract class Experiment extends Observable implements java.io.Serializable {

	private static final long serialVersionUID = -7079065801992566546L;
	protected File logFile;
	protected int threads;
	private boolean print;
	private boolean shutDownWhenFinished;
	private String name;
	
	private int index;
	
	private Experiment nextExperiment;
	
	public Experiment(String name, int threads) 
	{
		super();
		this.print = true;
		this.name = name;
		this.threads = threads;
		this.nextExperiment = null;
		this.logFile = null;
		this.shutDownWhenFinished = false;
	}
	
	public Experiment(String name, int threads, boolean print) 
	{
		super();
		this.print = print;
		this.name = name;
		this.threads = threads;
		this.nextExperiment = null;
		this.logFile = null;
		this.shutDownWhenFinished = false;
	}
	
	public static void printMeanTimes(int[] times)
	{
		for (int i = 0; i < times.length; i++)
		{
			System.out.println(i+"\t"+times[i]);
		}
	}
	
	public static long getMeanTime(long[] times)
	{
		long mean = 0;
		for (int i = 0; i < times.length; i++)
		{
			mean += times[i];
		}
		return mean/times.length;
	}
	
	public static float getRating(int victories, long meanTime, long meanTimeTurn1, long meanTimeTurn2)
	{
		float v = (float) victories;
		return (v*1000)/(meanTime+meanTimeTurn1+meanTimeTurn2+1);
	}
	
	public File getLogFile()
	{
		return logFile;
	}

	public void setLogFile(File logFile) 
	{
		this.logFile = logFile;
	}
	
	public boolean isShutDownWhenFinished()
	{
		return shutDownWhenFinished;
	}

	public void setShutDownWhenFinished(boolean shutDownWhenFinished) 
	{
		this.shutDownWhenFinished = shutDownWhenFinished;
	}

	public int getThreads() 
	{
		return threads;
	}

	public void setThreads(int threads)
	{
		if (threads < 0)
			throw new IllegalArgumentException();
		this.threads = threads;
	}
	
	public boolean isPrint() 
	{
		return print;
	}

	public void setPrint(boolean print) 
	{
		this.print = print;
	}

	public void start()
	{
		index = 0;
		if (threads > 0)
		{
			ExperimentThread[] et = new ExperimentThread[threads];
			et[0] = new ExperimentThread(true);
			for (int i = 1; i < threads; i++)
			{
				et[i] = new ExperimentThread(false);
			}
			for (int i = 0; i < threads; i++)
			{
				et[i].start();
			}
		}
		else
		{
			execute();
			end();
			if (print)
				print();
			next();
		}
	}
	
	public String getName()
	{
		return name;
	}
	
	public String toString()
	{
		return name;
	}
	
	public abstract void addObserverRecursive(Observer o);
	
	public abstract int size();
	
	public abstract int totalSize();
	
	public abstract void printRecursive();
	
	protected abstract void execute();
	
	protected abstract void end();
	
	protected abstract void print();
	
	protected  void toFile()
	{
		try {
			ObjectOutputStream st = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(logFile)));
			st.writeObject(this);
			st.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	protected synchronized int getIndex() 
	{
		return index;
	}

	protected synchronized int getAndIncreaseIndex()
	{
		int old = index;
		index++;
		return old;
	}
	
	void setNext(Experiment next)
	{
		this.nextExperiment = next;
	}
	
	private void next()
	{
		if (nextExperiment != null)
		{
			nextExperiment.start();
		}
	}
	
	private void shutDown()
	{
		String shutdownCommand = "";
		String osName = System.getProperty("os.name");   

		if (osName.startsWith("Win")) 
		{
			shutdownCommand = "shutdown.exe -s -t 0";
		} 
		else if (osName.startsWith("Linux") || osName.startsWith("Mac")) 
		{
			shutdownCommand = "shutdown -h now";
		} 
		else
		{
			System.exit(0);
		}
		try {
			Runtime.getRuntime().exec(shutdownCommand);
		} catch (IOException e) { }
	    System.exit(0);
	}
	
	private class ExperimentThread extends Thread {
		
		private boolean supervisor;
	
		public ExperimentThread(boolean supervisor) 
		{
			super();
			this.supervisor = supervisor;
		}
		
		public void run()
		{
			execute();
			if (supervisor)
			{
				int threshold = size()+threads;
				while (getIndex() < threshold)
				{
					try {
						sleep(2000);
					} catch (InterruptedException e) { }
				}
				setChanged();
				notifyObservers();
				end();
				if (print)
					print();
				if (logFile != null)
					toFile();
				if (shutDownWhenFinished)
					shutDown();
				else
					next();
			}
		}
	}
}
